package com.meida.module.file.provider.oss;


import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.meida.common.constants.SettingConstant;
import com.meida.common.utils.ApiAssert;
import com.meida.common.utils.FlymeUtils;
import com.meida.common.utils.RedisUtils;
import com.meida.module.file.client.entity.SysFile;
import com.meida.module.file.client.vo.OssSetting;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Iterator;

/**
 * @author flyme
 */
@Component
@Slf4j
public class FileUtil {

    @Autowired
    private RedisUtils redisUtils;

    public OssSetting getOssSetting() {
        String v = redisUtils.getConfig(SettingConstant.LOCAL_OSS);
        ApiAssert.isNotEmpty("您还未配置本地文件存储服务", v);
        return JSONUtil.toBean(v, OssSetting.class);
    }

    /**
     * 文件路径上传
     *
     * @param file
     * @param key
     * @return
     */
    public String localUpload(MultipartFile file, String key, SysFile sysFile) {
        OssSetting os = getOssSetting();
        String day = DateUtil.format(DateUtil.date(), "yyyyMMdd");
        String path = os.getFilePath() + "/" + day + "/" + sysFile.getFileId();
        String filePath = path + "/" + key;
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File f = new File(filePath);
        if (!f.getParentFile().exists()) {
            f.getParentFile().mkdirs();
        }
        ApiAssert.isFalse("文件名已存在", f.exists());
        try {
            file.transferTo(f);
            if (FlymeUtils.isNotEmpty(sysFile)) {
                String smallPath = "";
                if (FileUtil.isPicture(getExtend(f.getName()))) {
                    if (f.length() > 51200) {
                        //大于50k时压缩图片
                        smallPath = os.getFilePath() + "/" + day + "/" + sysFile.getFileId() + "/" + "small";
                        File smallDir = new File(smallPath);
                        if (!smallDir.exists()) {
                            smallDir.mkdirs();
                        }
                        smallPath += "/" + key;
                        Thumbnails.of(filePath).scale(1f).toFile(smallPath);
                    }
                    //FileUtil.scale(filePath, smallPath + "/" + key, 1, false);
                    sysFile.setLocalPath(filePath).setSmallPath(smallPath);

                }
            }
        } catch (IOException e) {
            log.error(e.toString());
            ApiAssert.failure("文件格式错误,请修改为RGB格式");
        }
        return filePath;
    }

    /**
     * 缩放图像
     *
     * @param source 源图像文件地址
     * @param result 缩放后的图像地址
     * @param scale  缩放比例
     * @param flag   缩放选择:true 放大; false 缩小;
     */
    public static void scale(String source, String result, int scale, boolean flag) {
        try {
            // 读入文件
            BufferedImage src = ImageIO.read(new File(source));
            // 得到源图宽
            int width = src.getWidth();
            // 得到源图长
            int height = src.getHeight();
            if (flag) {
                // 放大
                width = width * scale;
                height = height * scale;
            } else {
                // 缩小
                width = width / scale;
                height = height / scale;
            }
            Image image = src.getScaledInstance(width, height, Image.SCALE_DEFAULT);
            BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Graphics g = tag.getGraphics();
            // 绘制缩小后的图
            g.drawImage(image, 0, 0, null);
            g.dispose();
            // 输出到文件流
            ImageIO.write(tag, "JPEG", new File(result));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取文件
     *
     * @param url
     * @param response
     */
    public void view(String url, HttpServletResponse response) {

        File file = new File(url);
        FileInputStream i = null;
        OutputStream o = null;

        try {
            i = new FileInputStream(file);
            o = response.getOutputStream();

            byte[] buf = new byte[1024];
            int bytesRead;

            while ((bytesRead = i.read(buf)) > 0) {
                o.write(buf, 0, bytesRead);
                o.flush();
            }

            i.close();
            o.close();
        } catch (IOException e) {
            log.error(e.toString());
            ApiAssert.failure("读取文件出错");
        }
    }

    /**
     * 重命名
     *
     * @param url
     * @param toKey
     * @return
     */
    public String renameFile(String url, String toKey) {

        String result = copyFile(url, toKey);
        deleteFile(url);
        return result;
    }

    /**
     * 复制文件
     *
     * @param url
     * @param toKey
     */
    public String copyFile(String url, String toKey) {

        File file = new File(url);
        FileInputStream i = null;
        FileOutputStream o = null;
        String result = "";
        try {
            i = new FileInputStream(file);
            o = new FileOutputStream(new File(file.getParentFile() + "/" + toKey));

            byte[] buf = new byte[1024];
            int bytesRead;

            while ((bytesRead = i.read(buf)) > 0) {
                o.write(buf, 0, bytesRead);
            }

            i.close();
            o.close();
            result = file.getParentFile() + "/" + toKey;
        } catch (IOException e) {
            log.error(e.toString());
            ApiAssert.failure("复制文件出错");
        }
        return result;

    }

    /**
     * 删除文件
     *
     * @param url
     */
    public void deleteFile(String url) {
        File file = new File(url);
        file.delete();
    }

    public static InputStream getInputStream(MultipartFile file) {
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return inputStream;
    }

    /**
     * 判断文件是否为图片文件(GIF,PNG,JPG)
     *
     * @param srcFileName
     * @return
     */
    public static boolean isImage(File srcFilePath) {
        FileInputStream imgFile = null;
        byte[] b = new byte[10];
        int l = -1;
        try {
            imgFile = new FileInputStream(srcFilePath);
            l = imgFile.read(b);
            imgFile.close();
        } catch (Exception e) {
            return false;
        }

        if (l == 10) {
            byte b0 = b[0];
            byte b1 = b[1];
            byte b2 = b[2];
            byte b3 = b[3];
            byte b6 = b[6];
            byte b7 = b[7];
            byte b8 = b[8];
            byte b9 = b[9];

            if (b0 == (byte) 'G' && b1 == (byte) 'I' && b2 == (byte) 'F') {
                return true;
            } else if (b1 == (byte) 'P' && b2 == (byte) 'N' && b3 == (byte) 'G') {
                return true;
            } else if (b6 == (byte) 'J' && b7 == (byte) 'F' && b8 == (byte) 'I' && b9 == (byte) 'F') {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * 判断文件是否为图片<br>
     */
    public static boolean isPicture(String extend) {
        // 文件名称为空的场合
        if (FlymeUtils.isEmpty(extend)) {
            // 返回不和合法
            return false;
        }
        // 声明图片后缀名数组
        String imgeArray[][] = {{"bmp", "0"}, {"dib", "1"}, {"gif", "2"}, {"jfif", "3"}, {"jpe", "4"}, {"jpeg", "5"}, {"jpg", "6"}, {"png", "7"}, {"tif", "8"}, {"tiff", "9"}, {"ico", "10"}};
        // 遍历名称数组
        for (int i = 0; i < imgeArray.length; i++) {
            // 判断单个类型文件的场合
            if (imgeArray[i][0].equals(extend.toLowerCase())) {
                return true;
            }
        }
        return false;
    }


    /**
     * 获取文件扩展名
     *
     * @param filename
     * @return
     */
    public static String getExtend(String filename) {
        return getExtend(filename, "");
    }

    /**
     * 获取文件扩展名
     *
     * @param filename
     * @return
     */
    public static String getExtend(String filename, String defExt) {
        if ((filename != null) && (filename.length() > 0)) {
            int i = filename.lastIndexOf('.');

            if ((i > 0) && (i < (filename.length() - 1))) {
                return (filename.substring(i + 1)).toLowerCase();
            }
        }
        return defExt.toLowerCase();
    }


    /**
     * @param file
     * @param key
     * @param sysFile
     * @param x
     * @param y
     * @param width
     * @param height
     * @描述 —— 裁剪图片
     */
    public String cutImage(MultipartFile file, String key, SysFile sysFile, int x, int y, int width, int height) {
        OssSetting os = getOssSetting();
        String day = DateUtil.format(DateUtil.date(), "yyyyMMdd");
        String path = os.getFilePath() + "/" + day + "/" + sysFile.getFileId();
        String filePath = path + "/" + key;
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File f = new File(filePath);
        if (!f.getParentFile().exists()) {
            f.getParentFile().mkdirs();
        }
        FileInputStream is = null;
        ImageInputStream iis = null;
        try {
            file.transferTo(f);
            // 读取图片文件
            is = new FileInputStream(filePath);
            // 获取文件格式
            String ext = getExtend(f.getName());
            // ImageReader解码指定格式
            Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(ext);
            ImageReader reader = it.next();
            // 获取图片流
            iis = ImageIO.createImageInputStream(is);
            // 输入源中的图像将只按顺序读取
            reader.setInput(iis, true);
            // 描述如何对流进行解码
            ImageReadParam param = reader.getDefaultReadParam();
            // 图片裁剪区域
            Rectangle rect = new Rectangle(x, y, width, height);
            // 提供一个 BufferedImage，将其用作解码像素数据的目标
            param.setSourceRegion(rect);
            // 使用所提供的 ImageReadParam 读取通过索引 imageIndex 指定的对象
            BufferedImage bi = reader.read(0, param);
            // 保存新图片

            //压缩图片
            String smallPath = os.getFilePath() + "/" + day + "/" + sysFile.getFileId() + "/" + "small";
            File smallDir = new File(smallPath + "/" + key);
            if (!smallDir.exists()) {
                smallDir.mkdirs();
            }
            ImageIO.write(bi, ext, smallDir);
            sysFile.setLocalPath(filePath).setSmallPath(smallPath + "/" + key);
            return filePath;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (iis != null) {
                    iis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }
    }


    /**
     * @param file
     * @param key
     * @param sysFile
     * @param base64Img
     * @描述 —— 裁剪图片
     */
    public String cutImageByBase64(MultipartFile file, String key, SysFile sysFile, String base64Img) {
        OssSetting os = getOssSetting();
        String day = DateUtil.format(DateUtil.date(), "yyyyMMdd");
        String path = os.getFilePath() + "/" + day + "/" + sysFile.getFileId();
        String filePath = path + "/" + key;
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File f = new File(filePath);
        if (!f.getParentFile().exists()) {
            f.getParentFile().mkdirs();
        }

        try {
            file.transferTo(f);
            String smallPath = os.getFilePath() + "/" + day + "/" + sysFile.getFileId() + "/" + "small" + "/" + key;
            Base64ToImage(base64Img, smallPath);
            sysFile.setLocalPath(filePath).setSmallPath(smallPath);
            return filePath;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * base64字符串转换成图片
     *
     * @param imgStr      base64字符串
     * @param imgFilePath 图片存放路径
     * @return
     */
    public static boolean Base64ToImage(String imgStr, String imgFilePath) {
        if (FlymeUtils.isEmpty(imgStr)) {
            return false;
        }
        imgStr = imgStr.substring(imgStr.indexOf(",") + 1);
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            // Base64解码
            byte[] b = decoder.decodeBuffer(imgStr);
            for (int i = 0; i < b.length; ++i) {
                // 调整异常数据
                if (b[i] < 0) {
                    b[i] += 256;
                }
            }
            //文件夹不存在则自动创建
            File tempFile = new File(imgFilePath);
            if (!tempFile.getParentFile().exists()) {
                tempFile.getParentFile().mkdirs();
            }

            OutputStream out = new FileOutputStream(imgFilePath);
            out.write(b);
            out.flush();
            out.close();

            return true;
        } catch (Exception e) {
            return false;
        }

    }
}
